#ifndef _RETRO_DISPLAYDEVICE
#define _RETRO_DISPLAYDEVICE

#define RETRO_FONTTOP           DT_TOP
#define RETRO_FONTLEFT          DT_LEFT
#define RETRO_FONTCENTER        DT_CENTER
#define RETRO_FONTRIGHT         DT_RIGHT
#define RETRO_FONTVCENTER       DT_VCENTER
#define RETRO_FONTBOTTOM        DT_BOTTOM
#define RETRO_FONTWORDBREAK     DT_WORDBREAK
#define RETRO_FONTSINGLELINE    DT_SINGLELINE
#define RETRO_FONTEXPANDTABS    DT_EXPANDTABS
#define RETRO_FONTTABSTOP       DT_TABSTOP
#define RETRO_FONTNOCLIP        DT_NOCLIP

#define RETRO_WEIGHTTHIN        FW_THIN
#define RETRO_WEIGHTEXTRALIGHT  FW_EXTRALIGHT
#define RETRO_WEIGHTLIGHT       FW_LIGHT
#define RETRO_WEIGHTNORMAL      FW_NORMAL
#define RETRO_WEIGHTMEDIUM      FW_MEDIUM
#define RETRO_WEIGHTSEMIBOLD    FW_SEMIBOLD
#define RETRO_WEIGHTBOLD        FW_BOLD
#define RETRO_WEIGHTEXTRABOLD   FW_EXTRABOLD
#define RETRO_WEIGHTHEAVY       FW_HEAVY

namespace Retro
{
  namespace Device
  {
    namespace Display
    {
      class DisplayDevice;

      enum MatrixType
      {
        MatrixInvalid,
        MatrixWorld,
        MatrixView,
      };

      enum DisplayMode
      {
        DisplayInvalid,
        DisplayWindowed,
        DisplayFullscreen,
      };

      enum RenderEffect
      {
        EffectInvalid,
        EffectNormal,
        EffectDarken,
        EffectLighten,
      };

      enum RenderMode
      {
        RenderNone,
        RenderScene,
        RenderTexture,
        RenderBatch,
        RenderLine,
        RenderFont,
      };

      struct DLLEXPORT Color
      {
        COLOR value;

        INL operator COLOR()
        {
          return value;
        }
      };

      class DLLEXPORT Texture : public FileResource
      {
      protected:
        UINT        mWidth;
        UINT        mHeight;
        UINT        mTexWidth;
        UINT        mTexHeight;

      public:
        INL UINT    getWidth()
        {
          return mWidth;
        }

        INL UINT    getHeight()
        {
          return mHeight;
        }

        INL UINT    getTextureWidth()
        {
          return mTexWidth;
        }

        INL UINT    getTextureHeight()
        {
          return mTexHeight;
        }

        INL Texture(LibraryType iLibrary) :
            FileResource(iLibrary, ResourceTexture)
        {
        }
      };

      class DLLEXPORT Font : public Resource
      {
      protected:
        TCHAR       mFacename[RETRO_MAXTEXT];
        UINT        mHeight;
        UINT        mWeight;
        BOOL        mItalic;

      public:
        INL Font(LibraryType iLibrary) :
            Resource(iLibrary, ResourceFont)
        {
        }

        INL PCTSTR  getFacename()
        {
          return mFacename;
        }

        INL UINT    getHeight()
        {
          return mHeight;
        }

        INL UINT    getWeight()
        {
          return mWeight;
        }

        INL BOOL    isItalic()
        {
          return mItalic;
        }

        INL VOID    setFacename(PCTSTR iValue)
        {
          _tcscpy_s(mFacename, RETRO_MAXTEXT, iValue);
        }

        INL VOID    setHeight(UINT iValue)
        {
          mHeight = iValue;
        }

        INL VOID    setWeight(UINT iValue)
        {
          mWeight = iValue;
        }

        INL VOID    setItalic(BOOL iValue)
        {
          mItalic = iValue;
        }
      };

      class DLLEXPORT ResourceBuffer
      {
      private:
        Generic::Vector<Resource*, 10> mBuffer;

      public:
        UINT        clean();
        VOID        clear();

        INL UINT    add(Resource* iResource)
        {
          return mBuffer.add(iResource);
        }

        INL UINT    getLength()
        {
          return mBuffer.getLength();
        }

        BOOL        remove(INT iIndex);
        BOOL        remove(Resource* iValue);
        BOOL        removeFromID(INT iID);
        BOOL        removeFromResourceID(INT iID);
        BOOL        removeFromName(PCTSTR iName);

        Resource*   getFromID(INT iID);
        Resource*   getFromResourceID(INT iID);
        Resource*   getFromResourceName(PCTSTR iName);

        INL Resource* get(INT iIndex)
        {
          return mBuffer[iIndex];
        }

        INL Resource* operator[](INT iIndex)
        {
          return mBuffer[iIndex];
        }

        ~ResourceBuffer();
      };

      class DLLEXPORT DisplayDevice : public IDevice
      {
      private:
        HWND             mWindow;
        UINT             mWidth;
        UINT             mHeight;

      protected:
        ResourceBuffer   mBuffer;
        Texture*         mBatchTexture;
        RenderMode       mMode;

      public:
        virtual Font*    createFont(
          PCTSTR iFacename,
          UINT iHeight,
          UINT iWeight,
          BOOL iItalic
          ) = 0;
        virtual Texture* createTexture(
          PCTSTR iPath
          ) = 0;

        INL RenderMode getRenderMode()
        {
          return mMode;
        }

        INL HWND getWindow()
        {
          return mWindow;
        }

        INL VOID setWindow(HWND iWindow)
        {
          mWindow = iWindow;
        }

        INL UINT getWidth()
        {
          return mWidth;
        }

        INL UINT getHeight()
        {
          return mHeight;
        }

        INL VOID setWidth(UINT iValue)
        {
          mWidth = iValue;
        }

        INL VOID setHeight(UINT iValue)
        {
          mHeight = iValue;
        }

        INL ResourceBuffer& getBuffer()
        {
          return mBuffer;
        }

        virtual BOOL  drawTexture(
          Texture* iTexture,
          const Geom::Bounds& iDest,
          const Geom::Bounds* iSource,
          COLOR iColorTL,
          COLOR iColorTR,
          COLOR iColorBL,
          COLOR iColorBR
          ) = 0;
        INL BOOL      drawTexture(
          Texture* iTexture,
          const Geom::Bounds& iDest,
          const Geom::Bounds* iSource,
          COLOR iColor = 0xFFFFFFFF
          )
        {
          return drawTexture(
            iTexture,
            iDest,
            iSource,
            iColor,
            iColor,
            iColor,
            iColor);
        }
        INL BOOL      drawTexture(
          Texture* iTexture,
          const Geom::Point& iDest,
          const Geom::Bounds* iSource,
          COLOR iColorTL,
          COLOR iColorTR,
          COLOR iColorBL,
          COLOR iColorBR
          )
        {
          return drawTexture(
            iTexture,
            Geom::Bounds::get(
            iDest.x,
            iDest.y,
            iSource ? iSource->cx : iTexture->getWidth(),
            iSource ? iSource->cy : iTexture->getHeight()
            ),
            iSource ? iSource :
            &Geom::Bounds::get(
            0,
            0,
            iTexture->getWidth(),
            iTexture->getHeight()
            ),
            iColorTL,
            iColorTR,
            iColorBL,
            iColorBR
            );
        }
        INL BOOL      drawTexture(
          Texture* iTexture,
          const Geom::Point& iDest,
          const Geom::Bounds* iSource,
          COLOR iColor = 0xFFFFFFFF
          )
        {
          return drawTexture(
            iTexture,
            Geom::Bounds::get(
            iDest.x,
            iDest.y,
            iSource ? iSource->cx : iTexture->getWidth(),
            iSource ? iSource->cy : iTexture->getHeight()
            ),
            iSource ? iSource :
            &Geom::Bounds::get(
            0,
            0,
            iTexture->getWidth(),
            iTexture->getHeight()
            ),
            iColor
            );
        }

        virtual BOOL  drawString(
          Font* iFont,
          LPCTSTR iText,
          DWORD iFormat,
          DWORD iColor,
          const Geom::Bounds& iDest
          ) = 0;

        virtual BOOL  fillRectangle(
          const Geom::Bounds& iDest,
          COLOR iColorTL,
          COLOR iColorTR,
          COLOR iColorBL,
          COLOR iColorBR
          ) = 0;

        INL BOOL      fillRectangle(
          const Geom::Bounds& iDest,
          COLOR iColor = 0xFFFFFFFF
          )
        {
          return fillRectangle(
            iDest,
            iColor,
            iColor,
            iColor,
            iColor);
        }

        virtual BOOL  drawRectangle(
          const Geom::Bounds& iDest,
          COLOR iColor = 0xFFFFFFFF
          ) = 0;
        virtual BOOL  drawLine(
          LONG iX1,
          LONG iY1,
          LONG iX2,
          LONG iY2,
          COLOR iColor = 0xFFFFFFFF
          ) = 0;

        virtual VOID  resetTransform(
          MatrixType iType = MatrixWorld
          ) = 0;
        virtual VOID  transform(
          const Vector::Vector2D& iScaleCenter,
          const Vector::Vector2D& iScale,
          const Vector::Vector2D& iRotateCenter,
          REAL iRotate,
          const Vector::Vector2D& iTranslate,
          MatrixType iType = MatrixWorld
          ) = 0;

        virtual VOID  setAlpha(BYTE iValue) = 0;
        virtual VOID  setLightness(BYTE iValue) = 0;

        virtual VOID  setRenderEffect(RenderEffect iValue) = 0;
        virtual VOID  setRenderTarget(Texture* iValue) = 0;

        virtual BOOL  isWireframe() = 0;
        virtual BOOL  setWireframe(BOOL iValue) = 0;

        virtual DisplayMode getDisplayMode() = 0;
        virtual BOOL        setDisplayMode(DisplayMode iValue) = 0;

        virtual BOOL  getDisplaySize(Geom::Size& oSize) = 0;

        virtual BOOL  beginScene() = 0;
        virtual BOOL  endScene() = 0;

        virtual BOOL  beginTexture() = 0;
        virtual BOOL  beginLine(
          FLOAT iWidth = 1.0f,
          DWORD iPattern = 0xFFFFFFFF,
          FLOAT iPatternScale = 1.0f
          ) = 0;
        virtual BOOL  beginFont() = 0;
        virtual BOOL  beginBatch(Texture* iTexture) = 0;
        virtual BOOL  end() = 0;

        virtual BOOL  clear(COLOR iValue) = 0;

        virtual BOOL  addBatch(
          const Geom::Bounds& iDest,
          const Geom::Bounds* iSource,
          COLOR iColorTL,
          COLOR iColorTR,
          COLOR iColorBL,
          COLOR iColorBR
          ) = 0;
        INL BOOL      addBatch(
          const Geom::Bounds& iDest,
          const Geom::Bounds* iSource,
          COLOR iColor = 0xFFFFFFFF
          )
        {
          return addBatch(
            iDest,
            iSource,
            iColor,
            iColor,
            iColor,
            iColor);
        }
        INL BOOL      addBatch(
          const Geom::Point& iDest,
          const Geom::Bounds* iSource,
          COLOR iColorTL,
          COLOR iColorTR,
          COLOR iColorBL,
          COLOR iColorBR
          )
        {
          return addBatch(
            Geom::Bounds::get(
            iDest.x,
            iDest.y,
            iSource ? iSource->cx : mBatchTexture->getWidth(),
            iSource ? iSource->cy : mBatchTexture->getHeight()
            ),
            iSource ? iSource :
            &Geom::Bounds::get(
            0,
            0,
            mBatchTexture->getWidth(),
            mBatchTexture->getHeight()
            ),
            iColorTL,
            iColorTR,
            iColorBL,
            iColorBR
            );
        }
        INL BOOL      addBatch(
          const Geom::Point& iDest,
          const Geom::Bounds* iSource,
          COLOR iColor = 0xFFFFFFFF
          )
        {
          return addBatch(
            Geom::Bounds::get(
            iDest.x,
            iDest.y,
            iSource ? iSource->cx : mBatchTexture->getWidth(),
            iSource ? iSource->cy : mBatchTexture->getHeight()
            ),
            iSource ? iSource :
            &Geom::Bounds::get(
            0,
            0,
            mBatchTexture->getWidth(),
            mBatchTexture->getHeight()
            ),
            iColor
            );
        }
        virtual BOOL  addBatch(
          const Geom::Bounds& iDest,
          COLOR iColorTL,
          COLOR iColorTR,
          COLOR iColorBL,
          COLOR iColorBR
          )
        {
          return addBatch(
            iDest,
            NULL,
            iColorTL,
            iColorTR,
            iColorBL,
            iColorBR);
        }
        INL BOOL      addBatch(
          const Geom::Bounds& iDest,
          COLOR iColor = 0xFFFFFFFF
          )
        {
          return addBatch(
            iDest,
            NULL,
            iColor,
            iColor,
            iColor,
            iColor);
        }

        INL DisplayDevice(LibraryType iLibrary) :
          IDevice(DeviceDisplay, iLibrary), mMode(RenderNone)
        {
        }
        INL virtual ~DisplayDevice() {}
      };

      DLLEXPORT DisplayDevice* CreateDevice(LibraryType iLibrary);
    } // namespace Display
  } // namespace Device
} // namespace Retro

#endif
